package com.gitee.huanminabc.utils_tools.fast_cache.memory;

import com.gitee.huanminabc.utils_tools.fast_cache.AbstractCache;
import com.gitee.huanminabc.utils_tools.fast_cache.Cache;
import com.gitee.huanminabc.utils_tools.fast_cache.CacheObj;
import lombok.extern.slf4j.Slf4j;

import java.lang.ref.SoftReference;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
import java.util.TreeSet;


/**
 * 自动适配缓存清理:
 * <p>1. 优先时间过期和gc自动回收</p>
 * <p>2. 容量快满了时，清除没有使用的缓存 也就是访问次数为0的缓存</p>
 * <p>3. 容量满了时，清除最早访问的缓存,直到满足80%后停止</p>
 * <p>4. 以上策略每隔5分钟执行一次</p>
 * @param <V>
 */

@Slf4j
public abstract class AbstractMemoryCache<V> extends AbstractCache<V> implements Cache<String, V> {
    protected AbstractMemoryCache(int capacity) {
        super(capacity);

    }

    protected AbstractMemoryCache(int capacity, int timeout) {
        super(capacity, timeout);
    }

    @Override
    public CacheObj<String, V> putInside(String key, V value) {
        CacheObj<String, V> stringVCacheObj = new CacheObj<>(key, value);
        stringVCacheObj.setTtl(timeout + System.currentTimeMillis());
        SoftReference<CacheObj<String, V>> softReference = new SoftReference<>(stringVCacheObj);
        cacheMap.put(key, softReference);
        return stringVCacheObj;
    }

    @Override
    public CacheObj<String, V> putInside(String key, V value, long timeout) {
        CacheObj<String, V> stringVCacheObj = new CacheObj<>(key, value);
        stringVCacheObj.setTtl(timeout + System.currentTimeMillis());
        SoftReference<CacheObj<String, V>> softReference = new SoftReference<>(stringVCacheObj);
        cacheMap.put(key, softReference);
        return stringVCacheObj;
    }

    public void putInside(CacheObj<String, V> cacheObj) {
        SoftReference<CacheObj<String, V>> softReference = new SoftReference<>(cacheObj);
        cacheMap.put(cacheObj.getKey(), softReference);
    }

    @Override
    public V getInside(String key) {
        SoftReference<CacheObj<String, V>> cacheObjSoftReference = cacheMap.get(key);
        if (cacheObjSoftReference == null) {
            return null;
        }
        CacheObj<String, V> stringVCacheObj = cacheObjSoftReference.get();
        if (stringVCacheObj == null) {
            return null;
        }
        stringVCacheObj.accessCount.incrementAndGet();
        stringVCacheObj.lastAccess = System.currentTimeMillis();
        //判断是否过期
        if (stringVCacheObj.isExpired()){
            cacheMap.remove(key);
            return null;
        }
        return stringVCacheObj.getObj();
    }

    @Override
    public V removeInside(String key) {
        SoftReference<CacheObj<String, V>> remove = cacheMap.remove(key);
        if (remove == null) {
            return null;
        }
        CacheObj<String, V> stringVCacheObj = remove.get();
        if (stringVCacheObj == null) {
            return null;
        }
        return stringVCacheObj.getObj();
    }

    @Override
    public void clearInside() {
        cacheMap.clear();
    }

    protected  synchronized void prune() {

        Iterator<Map.Entry<String, SoftReference<CacheObj<String, V>>>> iterator = cacheMap.entrySet().iterator();
        while (iterator.hasNext()) {
            SoftReference<CacheObj<String, V>> value= iterator.next().getValue();
            //1.gc回收   清理value为null的 ,一但value为null，说明已经被gc回收了
            CacheObj<String, V> stringVCacheObj = value.get();
            if (stringVCacheObj == null) {
                iterator.remove();
                continue;
            }
            //跳过小于0的
            if (stringVCacheObj.getTtl() < 0) {
                continue;
            }

            //2.先清理缓存过期的
            if (stringVCacheObj.isExpired()) {
                iterator.remove();
                continue;
            }

            //3.如果内存快满了，清理没有使用的缓存
            if (isNearlyFull()) {
                //删除访问次数为0的
                if (stringVCacheObj.accessCount.get() == 0) {
                    cacheMap.remove(stringVCacheObj.getKey());
                }
            }
            stringVCacheObj = null;
        }

        //4.如果内存满了,按照访问时间来排序，删除最早访问的,直到满足80%
        if (isFull()) {
            Set<Map.Entry<String, SoftReference<CacheObj<String, V>>>> entries = cacheMap.entrySet();
            //从小到大排序 ,最早访问的在前面
            TreeSet<Map.Entry<String, SoftReference<CacheObj<String, V>>>> treeSet = new TreeSet<>((o1, o2) -> {
                CacheObj<String, V> stringVCacheObj1 = o1.getValue().get();
                CacheObj<String, V> stringVCacheObj2 = o2.getValue().get();
                if (stringVCacheObj1 == null || stringVCacheObj2 == null) {
                    return 0;
                }
                if (stringVCacheObj1.getLastAccess() == stringVCacheObj2.getLastAccess()) {
                    return 0;
                }
                return (int) (stringVCacheObj1.getLastAccess() - stringVCacheObj2.getLastAccess());
            });

            treeSet.addAll(entries);
            //删除最早访问的
            for (Map.Entry<String, SoftReference<CacheObj<String, V>>> entry : treeSet) {
                //跳过小于0的
                CacheObj<String, V> stringVCacheObj = entry.getValue().get();
                if (stringVCacheObj != null && stringVCacheObj.getTtl() < 0) {
                    continue;
                }
                cacheMap.remove(entry.getKey());
                if (isHalfFull()) {
                    break;
                }
            }
            treeSet.clear();
        }
        System.gc();//手动gc
    }


}
